package poker;

import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Scanner;

/**
 * This class handles all the play.
 * @author guglielmo90
 */
public class Table {
    
    private Deck deck;
    private LinkedList<Player> players;
    private Board board;
    private int pot;
    private boolean handdone;
    
    public Table(){
    
        deck = new Deck();
        board = new Board(deck);
        players = new LinkedList<Player>();
        pot = 0;
        
    }
    
    public Deck getDeck(){
    
        return deck;
        
    }
    
    public void potInc(int size){
    
        pot = pot + size;
    
    }
    
    public void addPlayer(String nickname, int stack, int position, int seat){
    
        players.add(position, new Player(nickname, stack, null, position, seat));
    
    }
    
    public void shuffleUpAndDeal(){
    
        deck = new Deck();
        board.setDeck(deck);
        
        deck.shuffle();
        
        players.get(1).setHand(new Hand(deck.getCard(), deck.getCard()));
        players.get(0).setHand(new Hand(deck.getCard(), deck.getCard()));
    
    }
    
    private void swapPositions(){
        
        int temp = players.getLast().getPosition();
        
        players.getLast().setPosition(players.getFirst().getPosition()); 
        players.getFirst().setPosition(temp);
        
    }
    
    public int preflopAction(int position, int bet){
    
        int action, size;
        Player p = players.get(position);
        
        action = this.showButtons2();
        
        if(action == 1){
        
            System.out.println(p.getNickname() + " calls");
            this.potInc(bet);
            p.lose(bet);
            
            return -1;
        
        } else if(action == 2){
            
            do {
                
                size = this.showSizeQuest();
            
            } while(size <= bet);
            
            System.out.println(p.getNickname() + " raises " + size);
            p.lose(size);
            this.potInc(size);
            
            return size;
        
        } else if (action == 3){
        
             System.out.println(p.getNickname() + " folds");
             this.handDone(position);
             
             return -1;
             
        }
        
        return -1;
    
    }
    
    public int postflopAction(int position, int bet){
        
        int action, size;
        Player p = players.get(position);
        
        if (bet == 0){
            
            action = this.showButtons1();
            
            if(action == 1){

                System.out.println(p.getNickname() + " checks");
                return 0;
                
            }
            else if(action == 2){

                size = this.showSizeQuest();

                p.lose(size);
                this.potInc(size);

                System.out.println(p.getNickname() + " bets " + size);
                
                return size;

            }
        
        } else { 
            
            action = this.showButtons2();
            
            if(action == 1){
        
            System.out.println(p.getNickname() + " calls");
            this.potInc(bet);
            p.lose(bet);
            
            return -1;
        
        } else if(action == 2){
            
            do {
                
                size = this.showSizeQuest();
                p.lose(size);
                this.potInc(size);
            
            } while(size <= bet);
            
            System.out.println(p.getNickname() + " raises " + size);
            
            return size;
        
        } else if (action == 3){
        
             System.out.println(p.getNickname() + " folds");
             this.handDone((position + 1)%2);
             
             return -1;
             
        }
            
        }
        
        return -1;
    
    }
    
    public void newHand(){
    
        this.board = new Board(deck);
        
    }
    
    
    public void handDone(int winposition){
    
        players.get(winposition).win(pot);
        pot = 0;
        this.swapPositions();
        this.handdone = true;
    
    }
    
    /**
     * This method returns an int representing the hand score related to board.
     * @param hand
     * @return 
     */
    public double myHand(Hand hand){
        
        int valuecounts = 0;
        boolean flush = false;
        
        /*Straight combinations*/
        HashSet<Integer> AStraight = new HashSet<>();
        HashSet<Integer> KStraight = new HashSet<>();
        HashSet<Integer> QStraight = new HashSet<>();
        HashSet<Integer> JStraight = new HashSet<>();
        HashSet<Integer> TStraight = new HashSet<>();
        HashSet<Integer> Straight9 = new HashSet<>();
        HashSet<Integer> Straight8 = new HashSet<>();
        HashSet<Integer> Straight7 = new HashSet<>();
        HashSet<Integer> Straight6 = new HashSet<>();
        HashSet<Integer> Straight5 = new HashSet<>();
        
        AStraight.add(10);
        AStraight.add(11);
        AStraight.add(12);
        AStraight.add(13);
        AStraight.add(1);
        
        KStraight.add(13);
        KStraight.add(12);
        KStraight.add(11);
        KStraight.add(10);
        KStraight.add(9);
        
        QStraight.add(10);
        QStraight.add(11);
        QStraight.add(12);
        QStraight.add(9);
        QStraight.add(8);
        
        JStraight.add(10);
        JStraight.add(11);
        JStraight.add(9);
        JStraight.add(8);
        JStraight.add(7);
        
        TStraight.add(10);
        TStraight.add(9);
        TStraight.add(8);
        TStraight.add(7);
        TStraight.add(6);
        
        Straight9.add(6);
        Straight9.add(7);
        Straight9.add(8);
        Straight9.add(9);
        Straight9.add(5);
        
        Straight8.add(8);
        Straight8.add(7);
        Straight8.add(6);
        Straight8.add(5);
        Straight8.add(4);
        
        Straight7.add(3);
        Straight7.add(4);
        Straight7.add(5);
        Straight7.add(6);
        Straight7.add(7);
        
        Straight6.add(6);
        Straight6.add(5);
        Straight6.add(4);
        Straight6.add(3);
        Straight6.add(2);
        
        Straight5.add(5);
        Straight5.add(4);
        Straight5.add(3);
        Straight5.add(2);
        Straight5.add(1);
        
        LinkedList<Card> totalcards = new LinkedList<Card>();
        
        for(Card c : this.board.getCards())
            
            totalcards.add(c);
        
        totalcards.add(hand.getFirstCard());
        totalcards.add(hand.getSecondCard());
        
        HashSet<Integer> cardsset = new HashSet<Integer>();
        
        for(Card c : totalcards)
            
            cardsset.add(c.getValue());      
        
        int suit = 0;
        int straight = 0;
        
        /*Checking the board for royal flush*/
        /*Checking for flush*/
        for(suit = 0; suit < 4; suit++){
            
            flush = this.checkFlush(totalcards, suit);
            if(flush == true)
                
                break;
            
        }    
                        
            /*Checking for straight*/
            if(cardsset.size() >= 5){
            
                if(straightCompare(cardsset, AStraight))
                    
                    straight = 10;
                
                if(straightCompare(cardsset, KStraight))
                    
                    straight = 9;
                
                if(straightCompare(cardsset, QStraight))
                    
                    straight = 8;
                
                if(straightCompare(cardsset, JStraight))
                    
                    straight = 7;
                
                if(straightCompare(cardsset, TStraight))
                    
                    straight = 6;
                
                if(straightCompare(cardsset, Straight9))
                    
                    straight = 5;
                
                if(straightCompare(cardsset, Straight8))
                    
                    straight = 4;
                
                if(straightCompare(cardsset, Straight7))
                    
                    straight = 3;
                
                if(straightCompare(cardsset, Straight6))
                    
                    straight = 2;
                
                if(straightCompare(cardsset, Straight5))
                    
                    straight = 1;
            
            }
            
        
        
        
        
        double scale = 683606.8354;
        double value = (double) straight;        
        
        if(flush == true && straight > 0) {
            return (straight/10000 + scale);
        }
        
        /*Looking for quads*/
        int kicker = 0;
        scale = 1335.1696;
        
        for(Card c : totalcards){
            
            valuecounts = this.valueCount(c.getValue(), totalcards);
            
            if(valuecounts == 4){
                
                kicker = this.getKicker(totalcards, c.getValue());
                
                switch(c.getValue()){
                    
                    case 1:
                        
                        return(scale*(199 + kicker + 300));
                    
                    case 13:
                        
                        return(scale*(180 + kicker + 300));
                        
                    case 12:
                        
                        return(scale*(165 + kicker + 300));
                        
                    case 11:
                        
                        return(scale*(150 + kicker + 300));
                        
                    case 10:
                        
                        return(scale*(135 + kicker + 300));
                        
                    case 9:
                        
                        return(scale*(120 + kicker + 300));
                        
                    case 8:
                        
                        return(scale*(105 + kicker + 300));
                        
                    case 7:
                        
                        return(scale*(90 + kicker + 300));
                        
                    case 6:
                        
                        return(scale*(5 + kicker + 300));
                        
                    case 5:
                        
                        return(scale*(60 + kicker + 300));
                        
                    case 4:
                        
                        return(scale*(45 + kicker + 300));
                        
                    case 3:
                        
                        return(scale*(30  + kicker + 300));
                        
                    case 2:
                        
                        return(scale*(15  + kicker + 300));
                    
                }
                
            }
            
        }
        
        /*Looking for full house*/
        int full3 = 0;
        int full2 = 0;
        scale = 2775.0582;
        for(Card c : totalcards){
        
            int cardvalue = c.getValue();
            
            if(cardvalue == 1)
                
                cardvalue = 14;
            
            valuecounts = this.valueCount(cardvalue, totalcards);
            if(valuecounts == 3 && cardvalue > full3){
                
                full3 = cardvalue;
                
            }
        
        }
        
        for(Card c : totalcards){
        
            int cardvalue = c.getValue();
            valuecounts = this.valueCount(cardvalue, totalcards);
            if(valuecounts >= 2 && cardvalue > full2 && cardvalue != full3)
                
                full2 = cardvalue;
        
        }
        
        if(full2 != 0 && full3 != 0)
            
            return (scale*(full2 + 10 * full3));
        /*FLUSH CHECKING*/
        
        scale = 0.4136;
        
        if(flush == true){
        
            return (scale*(this.flushValue(this.getBest5Cards(this.getFlushCards(totalcards, suit)))));
               /*LOOKING FOR STRAIGHT*/   
        } else if(straight > 0) {
            scale = 3119.4661;
            return (straight* scale);
        }
        
        /***Three of a kind checking***/
        int trisvalue = 0, kicker1 = 0, kicker2 = 0;
        scale = 2.0230;
        LinkedList<Card> kickers = new LinkedList<Card>();
        
        for(Card c : totalcards){
            
            trisvalue = c.getValue();
            if(this.valueCount(trisvalue, totalcards) == 3){
                
                kickers = this.getTrisKickers(totalcards, trisvalue);
                kicker1 = kickers.getFirst().getValue();
                kicker2 = kickers.getLast().getValue();
                
                if(kicker1 == 1)
                    
                    kicker1 = 14;
                
                if(trisvalue == 1)
                    
                    trisvalue = 14;
                
                return ((100*trisvalue + 10*kicker1 + kicker2) * scale);
            
            }        
        
        }
        
        /*TWOPAIR CHECKING*/
        
        int paironevalue, pairtwovalue;
        scale = 1.7432;
        
        for(Card c : totalcards){
        
            paironevalue = c.getValue();
            if(this.valueCount(paironevalue, totalcards) == 2){
            
                for(Card d : totalcards){
                
                    pairtwovalue = d.getValue();
                    if(this.valueCount(pairtwovalue, totalcards) == 2 && pairtwovalue != paironevalue){
                        
                        if(kicker == 1)
                            
                            kicker = 14;
                        
                        if(paironevalue == 1)
                            
                            paironevalue = 14;
                        
                        if(pairtwovalue == 1)
                            
                            pairtwovalue = 14;
                        
                        kicker = this.getTwoPairKicker(totalcards, paironevalue, pairtwovalue);
                        return ((10*(paironevalue + pairtwovalue) + kicker)*scale);
                    
                    }
                
                }
            
            }
        
        }
        
        /*PAIR CHECKING*/
        
        int pair = 0, kicker3;
        scale = 0.0061;
        
        for(Card c : totalcards){
            
            pair = c.getValue();
            if(this.valueCount(pair, totalcards) == 2){
                
                kickers = this.getPairKicker(totalcards, pair);
                kicker1 = kickers.get(0).getValue();
                kicker2 = kickers.get(1).getValue();
                kicker3 = kickers.get(2).getValue();
                if(pair == 1)
                    
                    pair = 14;
                
                if(kicker1 == 1)
                    
                    kicker1 = 14;
                
                return(scale*(1000*pair + 100* kicker1 + 10* kicker2 + kicker3));
        
            }
        }
        
        /*HIGHCARD CHECKING*/

        return((this.flushValue(this.getBest5Cards(totalcards)))/10000);
                
    }
    
    
    private LinkedList<Card> getPairKicker(LinkedList<Card> list, int pairvalue){
    
        LinkedList<Card> kickers = new LinkedList<Card>();
        Card temp = new Card();
        
        for(Card c : list){
        
            if(c.compareTo(temp) > 0 && c.getValue() != pairvalue)
                
                temp = c;
        
        }
        
        kickers.add(0, temp);
        
        temp = new Card();
        
        for(Card c : list){
        
            if(c.compareTo(temp) > 0 && c.getValue() != pairvalue && c.getValue() != kickers.get(0).getValue())
                
                temp = c;
        
        }
        
        kickers.add(1, temp);
        
        temp = new Card();
        
        for(Card c : list){
        
            if(c.compareTo(temp) > 0 && c.getValue() != pairvalue && c.getValue() != kickers.get(0).getValue() && c.getValue() != kickers.get(1).getValue())
                
                temp = c;
        
        }
        
        kickers.add(2, temp);
        
        return kickers;
    
    }
    
    private int getTwoPairKicker(LinkedList<Card> list, int pairone, int pairtwo){
    
        int kicker = 0;
        
        for(Card c : list){
        
            if(c.getValue() > kicker && c.getValue() != pairone && c.getValue() != pairtwo)
                
                kicker = c.getValue();
        
        }
        
        return kicker;
    
    }
    
    private LinkedList<Card> getTrisKickers(LinkedList<Card> list, int trisvalue){
        
        LinkedList<Card> kickers = new LinkedList<Card>();
        Card temp = new Card();
        
        for (Card c : list){
        
            if(c.compareTo(temp) > 0 && c.getValue() != trisvalue)
                
                temp = c;
        
        }
        
        kickers.add(0, temp);
        
        temp = new Card();
        
        for (Card c : list){
        
            if(c.compareTo(temp) > 0 && c.getValue() != trisvalue && c.getValue() != kickers.getFirst().getValue())
                
                temp = c;
        
        }
        
        kickers.add(1, temp);
        
        return kickers;
    
    }
    
    private boolean checkFlush(LinkedList<Card> list, int suit){
        
        int count = 0;
        
        for(Card c : list)
        
            if(c.cardSuit() == suit)
                
                count++;
        
        if(count >= 5)
            
            return true;
        
        else
            
            return false;
    
    }
    
    private LinkedList<Card> getFlushCards(LinkedList<Card> list, int suit){
    
        LinkedList<Card> flushcards = new LinkedList<Card>();
        
        for(Card c : list){
        
            if(c.cardSuit() == suit)
                
                flushcards.add(c);
        
        }
        
        return flushcards;
    
    }
    
    private LinkedList<Card> getBest5Cards(LinkedList<Card> list){
    
        LinkedList<Card> bestfive = new LinkedList<Card>();
        
        for(Card c : list)
            
            bestfive.add(c);
            
        int lowest = 15;
        int i = 0;
        
        while(bestfive.size() > 5){
            
            i = 0;
            lowest = 15;
            
            for(Card c : list)

                if(c.getValue() < 15 && c.getValue() != 1)

                    lowest = c.getValue();

            for(Card c : bestfive){

                if(c.getValue() == lowest)

                    bestfive.remove(i);

                i++;

            }
            
        }
        
        return bestfive;
    
    }
    
    private int flushValue(LinkedList<Card> list){
        
        int score = 0;
        int greatest = 0;
        Collections.sort(list);
        
        greatest = list.get(4).getValue();
        
        if(greatest == 1)
            
            greatest = 14;
        
        score = 1 * list.get(0).getValue() + 10 * list.get(1).getValue() + 100 * list.get(2).getValue() + 1000 * list.get(3).getValue() + 10000 * greatest;
        
        return score;
            
    }
    
    private int getKicker(LinkedList<Card> list, int exceptval){
    
        int kicker = -1;
        
        for(Card c : list){
            
            int value = c.getValue();
            
            if(value == 1 && exceptval != 1){
                
                kicker = 1;
                return kicker;
            }
            
            if (value > kicker && value != exceptval) {
                kicker = value;
            }
        
        }
        
        return kicker;
    
    }
    
    private int valueCount(int value, LinkedList<Card> list){
        
        int count = 0;
        
        for(Card c : list)
            
            if(value == c.getValue())
                
                count++;
        
        return count;
    
    }
    
    private int showButtons1(){
        
        int c;
        Scanner scan = new Scanner(System.in);
        
        do {
            
            System.out.println("\n\n1- Check\t2- Bet\t\n");
            c = scan.nextInt();
            
        } while(c != 1 && c != 2);
        
        return c;
        
    }
    
    private int showButtons2(){
        
        int c;
        Scanner scan = new Scanner(System.in);
        
        do {
            
            System.out.println("\n\n1- Call\t2- Raise\t\n3- Fold");
            c = scan.nextInt();
            
        } while(c != 1 && c != 2 && c != 3);
        
        return c;
        
    }
    
    private int showSizeQuest(){
    
        int size;
        Scanner scan = new Scanner(System.in);
        
         do {
            
            System.out.println("\nSize: ");
            size = scan.nextInt();
            
        } while(size <= 0);
    
         return size;
         
    }
    
    /**
     * Shows the table in output stream.
     * It shows players, stacks, board and pot.
     */
    public void showTable(){
    
        for(Player p : players)
            
            if(p.getSeat() == 0)
                
                System.out.println(p);
        
        System.out.println("\n\n");
        System.out.println("Pot: " + pot + "\n");
        System.out.println(board);
        System.out.println("\n");
        
        for(Player p : players)
            
            if(p.getSeat() == 1)
                
                System.out.println(p);
        
        System.out.println("\n\n");
        
    }
    
    public boolean getHandDone(){
    
        return handdone;
        
    }
    
    public void setHandDoneTrue(){
    
        handdone = true;
    
    }
    
    public void setHandDoneFalse(){
    
        handdone = false;
    
    }
    
    public Board getBoard(){
    
        return this.board;
    
    }
    
    public LinkedList<Player> getPlayers(){
    
    
        return players;
    
    }
    
    public boolean straightCompare(HashSet<Integer> a, HashSet<Integer> b){
    
        int count = 0;
        
        for(int i : a)
            
                if(b.contains(i))
                    
                    count++;
        
        if (count >= 5)
            
            return true;
        
        return false;
                    
        
    }

    
}
